package com.wang.jmonkey.modules.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.wang.jmonkey.common.constant.CacheConstant;
import com.wang.jmonkey.common.model.enums.BooleanEnum;
import com.wang.jmonkey.common.result.ServiceResult;
import com.wang.jmonkey.common.utils.UserUtil;
import com.wang.jmonkey.modules.system.mapper.SystemUserMapper;
import com.wang.jmonkey.modules.system.model.dto.SystemUserDto;
import com.wang.jmonkey.modules.system.model.entity.SystemUserEntity;
import com.wang.jmonkey.modules.system.model.enums.SystemAclPrincipalTypeEnum;
import com.wang.jmonkey.modules.system.model.param.SystemUserParam;
import com.wang.jmonkey.modules.system.model.param.SystemUserPasswordParam;
import com.wang.jmonkey.modules.system.model.query.SystemUserQuery;
import com.wang.jmonkey.modules.system.service.SystemAclService;
import com.wang.jmonkey.modules.system.service.SystemDaclService;
import com.wang.jmonkey.modules.system.service.SystemUserService;
import com.wang.jmonkey.redis.utils.LockUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.List;

/**
 * (SystemUser)表服务实现类
 *
 * @author HeJiawang
 * @since 2021-05-10 10:52:15
 */
@Service
public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemUserEntity> implements SystemUserService {

    @Autowired
    private SystemUserMapper mapper;

    @Autowired
    private SystemDaclService systemDaclService;

    @Autowired
    private SystemAclService systemAclService;


    @Autowired
    private RedisTemplate<String , Object> redis;

    /**
     * 用户信息密码加密规则
     */
    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();

    /**
     * 事务锁key <br/>
     */
    private static final String LOCK_KEY = "SYSTEM_USER_LOCK_KEY";

    /**
     * 分页查询用户信息
     * @param query query
     * @return PageInfo
     */
    @Override
    public PageInfo<SystemUserDto> page(SystemUserQuery query) {
        Page<SystemUserDto> list = mapper.page(query);
        return new PageInfo<>(list);
    }

    /**
     * 保存用户信息
     * @param user 用户信息
     * @return true 保存成功
     */
    @Transactional
    @Override
    public boolean save(SystemUserParam user) {
        try {
            LockUtil.lock(LOCK_KEY);

            // 校验用户登陆名称是否重复
            boolean isReuse = this.checkUsername(user);
            if (isReuse) return false;

            // 维护path字段
            String path = String.format(
                    "%s%s,",
                    StrUtil.isEmpty(UserUtil.getUser().getPath()) ? StrUtil.EMPTY : UserUtil.getUser().getPath(),
                    UserUtil.getUser().getId()
            );

            SystemUserEntity userEntity = user.convertToEntity().setPath(path);
            userEntity.setPassword( ENCODER.encode(userEntity.getPassword()));
            super.save(userEntity);

            user.setId(userEntity.getId());
            return systemDaclService.refreshDacl(user);
        } finally {

            if (LockUtil.isLocked(LOCK_KEY)) {
                LockUtil.unlock(LOCK_KEY);
            }
        }
    }

    /**
     * 修改用户信息
     * @param user 用户信息
     * @return true 修改成功
     */
    @Transactional
    @Override
    public boolean updateById(SystemUserParam user) {
        try {
            LockUtil.lock(LOCK_KEY);

            // 校验用户登陆名称是否重复
            boolean isReuse = this.checkUsername(user);
            if (isReuse) return false;

            String path = String.format(
                    "%s%s,",
                    StrUtil.isEmpty(UserUtil.getUser().getPath()) ? StrUtil.EMPTY : UserUtil.getUser().getPath(),
                    UserUtil.getUser().getId()
            );
            SystemUserEntity userEntity = user.convertToEntity().setPath(path);
            return super.updateById(userEntity) && systemDaclService.refreshDacl(user);
        } finally {

            if (LockUtil.isLocked(LOCK_KEY)) {
                LockUtil.unlock(LOCK_KEY);
            }
        }
    }


    /**
     * 校验用户登陆名称是否重复
     * @param userParam 用户登陆名称、用户id
     * @return true 用户登陆名称重复
     */
    @Override
    public boolean checkUsername(SystemUserParam userParam) {
        return mapper.checkUsername(userParam) > 0;
    }

    /**
     * 删除用户信息
     * @param id 用户信息id
     * @return true 删除成功
     */
    @Transactional
    @Override
    public boolean removeById(Serializable id) {
        try {
            LockUtil.lock(LOCK_KEY);

            SystemUserEntity userEntity = super.getById(id);
            if (ObjectUtil.isEmpty(userEntity)) return false;

            String username = userEntity.getUsername();
            redis.execute(new SessionCallback() {

                @Override
                public Object execute(RedisOperations operations) throws DataAccessException {
                    if (operations.opsForHash().hasKey(CacheConstant.PermissionCache.USER_INFO.getKey(), username)) {
                        operations.opsForHash().delete(CacheConstant.PermissionCache.USER_INFO.getKey(), username);
                    }

                    if (operations.opsForHash().hasKey(CacheConstant.PermissionCache.USER_PERMISSION.getKey(), username)) {
                        operations.opsForHash().delete(CacheConstant.PermissionCache.USER_PERMISSION.getKey(), username);
                    }

                    return null;
                }
            });

            return super.removeById(id)
                    && systemDaclService.deleteByUserId(id)
                    && systemAclService.deleteByPrincipalId(id, SystemAclPrincipalTypeEnum.USER);
        } finally {

            if (LockUtil.isLocked(LOCK_KEY)) {
                LockUtil.unlock(LOCK_KEY);
            }
        }
    }

    /**
     * 批次删除用户
     * @param userIdList 用户id集合
     * @return true 删除成功
     */
    @Override
    public boolean removeBatch(List<Long> userIdList) {
        if (CollUtil.isEmpty(userIdList)) return false;

        userIdList.forEach(userId -> this.removeById(userId) );
        return true;
    }

    /**
     * 修改用户密码
     * @param passwordParam 用户密码信息
     * @return true 修改成功
     */
    @Override
    public ServiceResult<Boolean> modifyPassword(SystemUserPasswordParam passwordParam) {
        SystemUserEntity user = super.getById(passwordParam.getId());

        ServiceResult<Boolean> result = new ServiceResult<>();
        /*if (!ENCODER.matches(passwordParam.getOldPassword(), user.getPassword())) {
            return result.error("原始密码输入错误！");
        }*/
        if (!passwordParam.getConfirmPassword().equals(passwordParam.getNewPassword())) {
            return result.error("确认密码与新密码不一致！");
        }

        user.setPassword(ENCODER.encode(passwordParam.getNewPassword()));
        return result.setSuccess(mapper.updatePassword(user) >= 0 );
    }

    /**
     * 锁定用户
     * @param id 用户id
     * @return true 锁定成功
     */
    @Override
    public boolean lock(Long id) {
        return mapper.updateLock(id, BooleanEnum.YES) > 0;
    }

    /**
     * 解锁用户
     * @param id 用户id
     * @return true 解锁成功
     */
    @Override
    public boolean unLock(Long id) {
        return mapper.updateLock(id, BooleanEnum.NO) > 0;
    }

    /**
     * 查看详情
     * @param id 用户id
     * @return 用户信息
     */
    @Override
    public SystemUserDto getDtoById(Long id) {
        return mapper.getDtoById(id);
    }
}
